package bootstrap

import (
	"context"
	"iris-starter/config"
	"time"

	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/middleware/logger"
	"github.com/kataras/iris/v12/middleware/recover"
	"github.com/kataras/iris/v12/sessions"

	"github.com/gorilla/securecookie"
)

const (
	// StaticAssets is the root directory for public assets like images, css, js.
	StaticAssets = "./public/"
	// Favicon is the relative 9to the "StaticAssets") favicon path for our app.
	Favicon = "favicon.ico"
)

type Bootstrapper struct {
	*iris.Application
	AppName  string
	Sessions *sessions.Sessions
}

type Configurator func(*Bootstrapper)

func New(conf *config.Config, cfgs ...Configurator) *Bootstrapper {
	app := &Bootstrapper{
		AppName:     conf.App.Name,
		Application: iris.New(),
	}

	for _, cfg := range cfgs {
		cfg(app)
	}

	return app
}

func (b *Bootstrapper) SetupViews(viewDir string) {
	b.RegisterView(iris.HTML(viewDir, ".html").Layout("shared/layout.html"))
}

func (b *Bootstrapper) SetupSessions(expire time.Duration, cookieHashKey, cookieBlockKey []byte) {
	b.Sessions = sessions.New(sessions.Config{
		Cookie:   "SECRET_SESS_COOKIE_" + b.AppName,
		Expires:  expire,
		Encoding: securecookie.New(cookieHashKey, cookieBlockKey),
	})
}

func (b *Bootstrapper) SetupErrorHandler() {
	b.OnAnyErrorCode(func(ctx iris.Context) {
		b.Application.Logger().Errorf("error occur:%#v", ctx.GetErr())
		ctx.StopWithError(iris.StatusBadRequest, ctx.GetErr())
		return
	})
}

func (b *Bootstrapper) Configure(cs ...Configurator) {
	for _, c := range cs {
		c(b)
	}
}

func (b *Bootstrapper) Bootstrap() *Bootstrapper {
	b.SetupViews("./views")
	b.SetupSessions(24*time.Hour, []byte("the-big-and-secret-fash-key-here"), []byte("lot-secret-of-characters-big-too"))
	b.SetupErrorHandler()
	b.Favicon(StaticAssets + Favicon)
	b.HandleDir("/public", iris.Dir(StaticAssets))

	b.Use(recover.New())
	b.Use(logger.New())
	return b
}

func (b *Bootstrapper) Listen(cfg ...iris.Configurator) {
	appConf := config.Conf.App
	signalChan := make(chan struct{})
	iris.RegisterOnInterrupt(func() {
		timeout := 10 * time.Second
		ctx, cancel := context.WithTimeout(context.Background(), timeout)
		defer cancel()
		b.Shutdown(ctx)
		close(signalChan)
	})
	b.Run(iris.Addr(appConf.Addr), cfg...)
	<-signalChan
}
